<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI 图像生成器</title>
    <style>
        :root {
            --primary-color: #6366f1;
            --secondary-color: #a855f7;
            --background: linear-gradient(135deg, #f0f4ff 0%, #fdf2ff 100%);
        }

        body {
            font-family: 'Segoe UI', system-ui, sans-serif;
            min-height: 100vh;
            margin: 0;
            padding: 2rem;
            background: var(--background);
            color: #334155;
        }

        .container {
            max-width: 800px;
            margin: 0 auto;
            background: rgba(255, 255, 255, 0.9);
            backdrop-filter: blur(10px);
            border-radius: 1.5rem;
            padding: 2rem;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
        }

        .header {
            text-align: center;
            margin-bottom: 2rem;
        }

        .title {
            font-size: 2.5rem;
            color: var(--primary-color);
            margin: 0 0 1rem;
            background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .input-group {
            display: flex;
            gap: 1rem;
            margin-bottom: 1.5rem;
        }

        #promptInput {
          width: 100%; /* 强制占满容器 */
          padding: 1rem;
          border: 2px solid #e2e8f0;
          border-radius: 0.75rem;
          font-size: 1rem;
          box-sizing: border-box; /* 防止溢出 */
        }

        #promptInput:focus {
            outline: none;
            border-color: var(--primary-color);
            box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
        }

        #generateBtn {
            padding: 1rem 2rem;
            background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
            color: white;
            border: none;
            border-radius: 0.75rem;
            font-weight: 600;
            cursor: pointer;
            transition: transform 0.2s, box-shadow 0.2s;
        }

        #generateBtn:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(99, 102, 241, 0.3);
        }

        #generateBtn:disabled {
            opacity: 0.7;
            cursor: not-allowed;
            background: #cbd5e1;
        }

        .result-card {
            margin-top: 2rem;
            background: white;
            border-radius: 1rem;
            overflow: hidden;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
            opacity: 0;
            transform: translateY(20px);
            animation: fadeIn 0.5s forwards;
        }

        @keyframes fadeIn {
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        #generatedImage {
            width: 100%;
            height: 512px;
            object-fit: cover;
            border-bottom: 1px solid #f1f5f9;
        }

        .prompt-display {
            padding: 1.5rem;
            background: #f8fafc;
            font-style: italic;
            color: #64748b;
        }

        .loading {
            text-align: center;
            padding: 2rem;
            color: #64748b;
        }

        .loader {
            width: 40px;
            height: 40px;
            margin: 0 auto 1rem;
            border: 4px solid #e2e8f0;
            border-top-color: var(--primary-color);
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }

        .error {
            padding: 1rem;
            background: #fee2e2;
            color: #dc2626;
            border-radius: 0.5rem;
            margin: 1rem 0;
            border: 1px solid #fca5a5;
        }

        @media (max-width: 640px) {
            .container {
                padding: 1.5rem;
                margin: 1rem;
            }
            
            .input-group {
                flex-direction: column;
            }
            
            #generateBtn {
                width: 100%;
            }
        }
        /* 新增样式 */
        .input-group {
          display: flex;
          flex-direction: column;
          gap: 1.5rem;
        }

        .input-row {
          width: 100%;
        }

        .params-grid {
          display: grid;
          grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
          gap: 1rem;
        }

        .param-item {
          display: flex;
          flex-direction: column;
          gap: 0.5rem;
        }

        .param-item label {
          font-size: 0.9rem;
          color: #64748b;
          font-weight: 500;
        }

        .input-wrapper {
          position: relative;
          display: flex;
          align-items: center;
        }

        .input-wrapper input[type="number"] {
          width: 100%;
          padding: 0.75rem;
          padding-right: 2.5rem;
          border: 2px solid #e2e8f0;
          border-radius: 0.75rem;
          font-size: 1rem;
        }

        .unit {
          position: absolute;
          right: 1rem;
          color: #94a3b8;
          font-size: 0.9rem;
          pointer-events: none;
        }

        .generate-wrapper {
          margin-top: 1rem;
          display: flex;
          justify-content: flex-end;
        }

        #generateBtn {
          padding: 1rem 2.5rem;
          background: linear-gradient(45deg, #6366f1, #a855f7);
          color: white;
          border: none;
          border-radius: 0.75rem;
          font-weight: 600;
          cursor: pointer;
          transition: transform 0.2s;
          display: flex;
          align-items: center;
          gap: 0.5rem;
        }

        #generateBtn:hover {
          transform: translateY(-2px);
        }

        /* 响应式设计 */
        @media (max-width: 640px) {
          .params-grid {
            grid-template-columns: 1fr;
          }
          
          .generate-wrapper {
            justify-content: center;
          }
        }


        .result-card {
          display: flex;
          justify-content: center; /* 水平居中 */
          align-items: center;     /* 垂直居中 */
          max-height: 768; /* 限制最大宽度 */
          width: auto;    /* 高度自适应 */
          background: linear-gradient(45deg, #6366f1, #a855f7);  /* 添加背景色避免透明区域视觉问题 */
        }

        #generatedImage {
/*          max-height: 100%; /* 图片不超出容器 */*/
          width: 100%;    /* 保持宽高比 */
          object-fit: contain; /* 完整显示图片 */
        }

    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1 class="title">AI 图像生成器</h1>
            <p>用文字描述你的想象，AI 帮你生成图片</p>
        </div>

        <div class="input-group">
          <!-- 主输入框 -->
          <div class="input-row">
            <input 
              type="text" 
              id="promptInput" 
              placeholder="示例：星际旅行中的机械城堡，赛博朋克风格，4K超清"
              autocomplete="off"
              value="a beautiful Chinese woman, Chinese HanFu, off_shoulders, decollete"
            >
          </div>

          <!-- 参数控制区 -->
          <div class="params-grid">
            <!-- 宽度 -->
            <div class="param-item">
              <label for="width">宽度</label>
              <div class="input-wrapper">
                <input type="number" id="width" name="width" min="0" max="4096" step="8" value="512">
                <span class="unit">px</span>
              </div>
            </div>

            <!-- 高度 -->
            <div class="param-item">
              <label for="height">高度</label>
              <div class="input-wrapper">
                <input type="number" id="height" name="height" min="0" max="4096" step="8" value="512">
                <span class="unit">px</span>
              </div>
            </div>

            <!-- 迭代步数 -->
            <div class="param-item">
              <label for="steps">迭代步数</label>
              <div class="input-wrapper">
                <input type="number" id="steps" name="steps" min="1" max="200" step="1" value="20">
              </div>
            </div>

            <!-- 引导系数 -->
            <div class="param-item">
              <label for="guidance">引导系数</label>
              <div class="input-wrapper">
                <input type="number" id="guidance" name="guidance" min="0" max="50" step="0.5" value="4">
              </div>
            </div>
          </div>

          <!-- 生成按钮 -->
          <div class="generate-wrapper">
            <button id="generateBtn" onclick="generateImage()">
              <span class="icon">🎨</span>
              立即生成
            </button>
          </div>
        </div>

        <div class="loading" id="loading">
            <div class="loader"></div>
            <p>AI 正在创作中，请稍候...</p>
        </div>

        <div class="error" id="error"></div>

        <div id="resultContainer"></div>
    </div>

    <script>
        const API_URL = 'http://127.0.0.1:8000/api/images/generate';
        const MAX_PROMPT_LENGTH = 500;
        let retryCount = 0;
        const MAX_RETRY = 2;

        // 元素引用对象
        const elements = {
            promptInput: document.getElementById('promptInput'),
            generateBtn: document.getElementById('generateBtn'),
            loading: document.getElementById('loading'),
            error: document.getElementById('error'),
            resultContainer: document.getElementById('resultContainer'),
            widthInput: document.getElementById('width'),
            heightInput: document.getElementById('height'),
            stepsInput: document.getElementById('steps'),
            guidanceInput: document.getElementById('guidance')
        };

        // 配置常量（可抽离到单独配置文件）
        const API_CONFIG = {
          MAX_RETRY: 1,
          TIMEOUT: 300000, // 300秒超时
          DEFAULT_PARAMS: {
            width: 512,
            height: 512,
            guidance_scale: 5, // 更合理的默认值
            num_inference_steps: 20, // 适当增加生成质量
            negative_prompt: 'something dad, something wrong, ugly, texts, blurry, low_res, low quality, b&w'
          }
        };

        // 图片验证正则（提前编译提升性能）
        const IMAGE_REGEX = new RegExp(
          `^data:image/(${['png', 'jpeg', 'webp'].join('|')});base64,`, 
          'i'
        );

        async function generateImage() {
          const { promptInput, error, resultContainer, widthInput, heightInput, stepsInput, guidanceInput } = elements;
          const prompt = promptInput.value.trim();
          const width = widthInput.value.trim();
          const height = heightInput.value.trim();
          const num_inference_steps = stepsInput.value.trim();
          const guidance_scale = guidanceInput.value.trim();
          
          try {
            // 重置状态
            clearUIState();
            
            // 输入验证
            const prompt_ = sanitizeInput(prompt);
            validatePrompt(prompt_);

            // 准备请求
            const controller = new AbortController();
            const timeoutId = setTimeout(
              () => controller.abort(), 
              API_CONFIG.TIMEOUT
            );

            toggleLoading(true);
            
            // 发送请求（抽离请求配置）
            const response = await fetch(API_URL, createRequestConfig({
              prompt,
              width,
              height,
              guidance_scale, // 更合理的默认值
              num_inference_steps, // 适当增加生成质量
              signal: controller.signal
            }));
            
            clearTimeout(timeoutId);
            
            // 处理响应
            const data = await processResponse(response);
            showResult(createImageData(data));

            retryCount = 0; // 重置重试计数器

          } catch (err) {
            await handleGenerationError(err);
          } finally {
            toggleLoading(false);
          }
        }

        /* ========== 工具函数 ========== */
        // 清理UI状态
        function clearUIState() {
          elements.error.textContent = '';
          elements.error.style.display = 'none';
          elements.resultContainer.innerHTML = '';
        }

        // 输入净化
        function sanitizeInput(text) {
          return text
            .replace(/<[^>]*>?/gm, '') // 移除HTML标签
            .substring(0, MAX_PROMPT_LENGTH);
        }

        // 输入验证
        function validatePrompt(prompt) {
          if (!prompt) throw new Error('请输入图片描述内容');
          if (prompt.length > MAX_PROMPT_LENGTH) {
            throw new Error(`描述内容过长（最大允许 ${MAX_PROMPT_LENGTH} 字符）`);
          }
        }

        // 将API返回的图片数据转换为可显示的格式
        function createImageData(data) {
          if (!data?.image) throw new Error("无效的图片数据");
          return data.image; // 假设API返回的图片数据直接是base64字符串
        }

        // 创建请求配置
        function createRequestConfig(override) {
          return {
            method: 'POST',
            headers: { 
              'Content-Type': 'application/json',
              'X-Request-ID': generateRequestId() 
            },
            body: JSON.stringify({
              ...API_CONFIG.DEFAULT_PARAMS,
              ...override,
              timestamp: Date.now()
            }),
            signal: override.signal
          };
        }

        // 处理响应
        async function processResponse(response) {
          if (!response.ok) {
            const errorData = await parseErrorResponse(response);
            throw new HttpError(response.status, errorData.message);
          }
          
          const data = await response.json();
          validateImageData(data.image);
          return data;
        }

        // 验证图片数据
        function validateImageData(imageData) {
          if (!IMAGE_REGEX.test(imageData)) {
            throw new ValidationError('无效的图片格式');
          }
          
          // 可选：验证Base64数据长度
          const base64Data = imageData.split(',')[1];
          if (!base64Data || base64Data.length < 100) {
            throw new ValidationError('图片数据不完整');
          }
        }

        // 错误处理
        async function handleGenerationError(err) {
          // 特定错误处理
          if (err.name === 'AbortError') {
            showError('请求超时，请稍后重试');
            return;
          }

          if (err instanceof HttpError) {
            showError(`服务器错误: ${err.message}`);
            return;
          }

          if (err instanceof ValidationError) {
            showError(`数据验证失败: ${err.message}`);
            return;
          }

          // 重试逻辑
          if (retryCount < API_CONFIG.MAX_RETRY) {
            retryCount++;
            await delay(1000 * retryCount);
            showError(`正在第 ${retryCount} 次重试...`);
            return generateImage();
          }

          // 最终错误处理
          showError(`生成失败: ${err.message || '未知错误'}`);
          logError(err);
        }

        /* ========== 工具类/辅助函数 ========== */
        // 自定义错误类型
        class HttpError extends Error {
          constructor(status, message) {
            super(message);
            this.status = status;
          }
        }

        class ValidationError extends Error {}

        // 解析错误响应
        async function parseErrorResponse(response) {
          try {
            return await response.json();
          } catch {
            return { message: '无法解析错误信息' };
          }
        }

        // 生成请求ID
        function generateRequestId() {
          return Math.random().toString(36).substr(2, 9);
        }

        // 延迟函数
        function delay(ms) {
          return new Promise(resolve => setTimeout(resolve, ms));
        }

        // 错误日志
        function logError(err) {
          console.error('[Image Generation Error]', {
            error: err.stack,
            timestamp: new Date().toISOString()
          });
        }

        // 应补充以下函数实现
        function toggleLoading(show) {
          elements.loading.style.display = show ? 'block' : 'none';
          elements.generateBtn.disabled = show;
        }

        function showResult(imageData) {
          const html = `
            <div class="result-card">
              <img id="generatedImage" src="${imageData}">
            </div>
            <div class="prompt-display">${elements.promptInput.value}</div>
            <button class="download-btn"
             onclick="downloadImage('${imageData}', '${elements.promptInput.value}')"
            >
             下载图片
            </button>
          `;
          elements.resultContainer.innerHTML = html;
        }

        // function showResult(data) {
        //     const { resultContainer } = elements;
            
        //     // 图片预加载
        //     const img = new Image();
        //     img.src = data.image;
            
        //     img.onload = () => {
        //         resultContainer.innerHTML = `
        //             <div class="result-card">
        //                 <img 
        //                     src="${data.image}" 
        //                     alt="生成图片：${data.prompt}"
        //                     loading="lazy"
        //                     onerror="handleImageError(this)"
        //                 >
        //                 <div class="prompt-display">
        //                     🎨 生成描述：${data.prompt}
        //                 </div>
        //                 <button 
        //                     class="download-btn"
        //                     onclick="downloadImage('${data.image}', '${data.prompt}')"
        //                 >
        //                     下载图片
        //                 </button>
        //             </div>
        //         `;
        //     };
            
        //     img.onerror = () => {
        //         showError('图片加载失败');
        //     };
        // }

        // function showError(message) {
        //   elements.error.textContent = message;
        //   elements.error.style.display = 'block';
        // }

        function handleError(err) {
            const errorMessage = err.name === 'AbortError' 
                ? '请求超时，请稍后重试' 
                : err.message;
            
            showError(`生成失败: ${errorMessage}`);
            console.error('生成错误:', err);
        }

        function showError(message) {
            const { error } = elements;
            error.textContent = message;
            error.style.display = 'block';
            
            // 自动隐藏
            if (message) {
                setTimeout(() => {
                    error.style.display = 'none';
                }, 5000);
            }
        }

        function downloadImage(base64Data, fileName = 'generated-image') {
            const link = document.createElement('a');
            link.href = base64Data;
            link.download = `${fileName.replace(/[^\w]/g, '_')}-${Date.now()}.png`;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }

        function handleImageError(imgElement) {
            imgElement.src = 'fallback-image.png';
            showError('图片加载失败，已显示备用图片');
        }

        // 事件监听
        elements.promptInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') generateImage();
        });
    </script>
</body>
</html>